#include <iostream>
#include <GL/glew.h>
#include <GL/glfw.h>
#include <glm/glm.hpp>

#define GLFW_CDECL

#include <AntTweakBar.h>
#include "Application.h"
#include "DrawEvent.h"
#include "UpdateEvent.h"
#include "Container.h"
#include "Boid.h"

using namespace std;


void Application::Init(){

		
	if(!glfwInit())
		cout << "Failed to initialize GLFW\n";
	
	if(!glfwOpenWindow(1024, 768, 0,0,0,0, 32,0, GLFW_WINDOW))
	{
		cout << "Failed to open GLFW window\n";
		glfwTerminate();
	}

	glewExperimental = GL_TRUE;

	if (glewInit() != GLEW_OK) 
		cout << "Failed to initialize GLEW\n";
	
	glfwSetWindowTitle("flocks");
	glfwEnable(GLFW_STICKY_KEYS);
	glfwEnable(GLFW_MOUSE_CURSOR);
    glfwEnable(GLFW_KEY_REPEAT);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(50.0, 1024/768, 2.0, 2000.0);

	glEnable(GL_LIGHTING);

	glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
	glEnable( GL_COLOR_MATERIAL );

	float lightPos[4] = {0, 10, 0, 0};
	glLightfv(GL_LIGHT0, GL_POSITION, lightPos);

	glEnable(GL_LIGHT0);

	TwInit(TW_OPENGL, NULL);	
	TwWindowSize(1024, 768);

	TwBar *bar;
	bar = TwNewBar("Options");

	TwAddSeparator(bar, NULL, " group='Camera' ");
	TwAddSeparator(bar, NULL, " group='Container' ");
	TwAddSeparator(bar, NULL, " group='Boid' ");
	TwAddSeparator(bar, NULL, " group='Flock' ");

	TwAddButton(bar, "draw walls", Container::changeDrawModeCallback, NULL, " group='Container' ");  

	TwAddVarCB(bar, "x coordinate", TW_TYPE_FLOAT, Application::setCameraXCallback, Application::getCameraXCallback, NULL, " group='Camera' step=0.1");
	TwAddVarCB(bar, "y coordinate", TW_TYPE_FLOAT, Application::setCameraYCallback, Application::getCameraYCallback, NULL, " group='Camera' step=0.1");
	TwAddVarCB(bar, "z coordinate", TW_TYPE_FLOAT, Application::setCameraZCallback, Application::getCameraZCallback, NULL, " group='Camera' step=0.5");

	TwAddVarCB(bar, "width", TW_TYPE_FLOAT, Container::setWidthCallback, Container::getWidthCallback, NULL, " group='Container' step=0.5 min=50");
	TwAddVarCB(bar, "height", TW_TYPE_FLOAT, Container::setHeightCallback, Container::getHeightCallback, NULL, " group='Container' step=0.5 min=50");
	TwAddVarCB(bar, "depth", TW_TYPE_FLOAT, Container::setDepthCallback,Container::getDepthCallback, NULL, " group='Container' step=0.5 min=50");
	TwAddVarCB(bar, "min distance", TW_TYPE_FLOAT, Container::setMinDistCallback,Container::getMinDistCallback, NULL, " group='Container' step=0.5 min=10");

	TwAddVarCB(bar, "boids", TW_TYPE_INT32, Application::setNumOfBoidsCallback, Application::getNumOfBoidsCallback, NULL, " group='Flock' step=1");
	TwAddVarCB(bar, "obstacle avoidance", TW_TYPE_FLOAT, Boid::setObstacleAvoidanceCoeffCallback, Boid::getObstacleAvoidanceCoeffCallback, NULL, " group='Flock' step=5");

	glfwSetMouseButtonCallback((GLFWmousebuttonfun)TwEventMouseButtonGLFW); 
	glfwSetMousePosCallback((GLFWmouseposfun)TwEventMousePosGLFW);          
	glfwSetMouseWheelCallback((GLFWmousewheelfun)TwEventMouseWheelGLFW);    
	glfwSetKeyCallback((GLFWkeyfun)TwEventKeyGLFW);                         
	glfwSetCharCallback((GLFWcharfun)TwEventCharGLFW);
	
	drawScene("scene.txt");

}

void Application::drawScene(const char* file_name){

	Container::get_Instance()->setWidth(100.f);
	Container::get_Instance()->setHeight(100.f);
	Container::get_Instance()->setDepth(100.f);
	//Container::get_Instance()->changeDrawMode();

	Boid *b = new Boid();
                     
}

void Application::Draw(){

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(camera.x, camera.y, camera.z,
          0.0, 0.0, 0.0,
          0.0, 1.0, 0.0);

	DrawEvent::drawAll();
}

void Application::Update(double h){

	UpdateEvent::updateAll(h);
}

void Application::Run(){
	float bgColor[] = {0.1f, 0.2f, 0.4f};
	double cur_time = glfwGetTime(), h = 0.015;

	while((glfwGetKey(GLFW_KEY_ESC) != GLFW_PRESS) && glfwGetWindowParam(GLFW_OPENED)){ 

		double time = glfwGetTime();
		h = time - cur_time;
		cur_time = time;
		if (h < 0.0001)
			h = 0.01;

		glClearColor(bgColor[0], bgColor[1], bgColor[2], 1);
        glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT );
		
		Update(h);
		Draw();
		
		TwDraw();
		glfwSwapBuffers();		
	}

	TwTerminate();
	glfwTerminate();
}

void Application::Destroy(){}

void Application::setNumOfBoids(int n){
	if (n > numOfBoids)
		for(int i = 0; i < n - numOfBoids; ++i)
			Boid *b = new Boid();
}

Application *Application::instance = NULL;